(function(){

	phnq.io =
	{
		nextExecId: 0,
		execResults: {},
		
		exec: function(cmdName, params, callback)
		{
			params = params || {};
			params.execId = this.nextExecId++;
			var uriBuf = [];
			uriBuf.push("exec/");
			uriBuf.push(cmdName);
			var c = ".js?";
			for(k in params)
			{
				uriBuf.push(c);
				uriBuf.push(escape(k));
				uriBuf.push("=");
				uriBuf.push(escape(params[k]));
				c = "&";
			}
			
			var uri = uriBuf.join("");
			
			if(callback)
			{
				loadScript(uri, function()
				{
					callback(phnq.io.execResults[params.execId]);
					delete phnq.io.execResults[params.execId];
				});
				return null;
			}
			else
			{
				var s = phnq.io.get("exec/"+cmdName+".js", params);
				eval(s);
				var result = phnq.io.execResults[params.execId];
				delete phnq.io.execResults[params.execId];
				return result;
			}
		},

		get: function(uri, params, callback)
		{
			var cbWrap = function(req)
			{
				callback(req.responseText);
			};
			
			var req = phnq.io.ajax(uri, params, {method:"GET", callback:callback?cbWrap:null});
			
			return callback ? null : req.responseText;
		},
		
		post: function(uri, params, callback)
		{
			return phnq.io.ajax(uri, params, {method:"POST", callback:callback});
		},
		
		ajax: function(uri, params, options)
		{
			return doAjaxReq(uri, params, options);
		},
		
		loadScript: function(uri, callback, options)
		{
			loadScript(uri, callback, options);
		}
	};

	var loadScript = function(uri, callback, options)
	{
		var retainScript = !!options && !!options.retainScript;

		var hs = document.getElementsByTagName("head");
		if(hs.length == 0)
			throw "Must have head element in document.";
			
		var h = hs[0];

		var s = document.createElement("script");
		
		var scriptWasLoaded = function(evt, hadError)
		{
			if(callback)
				callback(!hadError);
				
			if(!retainScript)
			{
				h.removeChild(s);
				s = null;
				h = null;
			}
		};
		
		var scriptLoadError = function(evt)
		{
			scriptWasLoaded(evt, true);
		};
		
		if(phnq.client.browser.IE)
		{
			s.onreadystatechange = function()
			{
				if(s.readyState == "loaded" || s.readyState == "complete")
					scriptWasLoaded();
			};
		}
		else
		{
			phnq.event.listen(s, "load", scriptWasLoaded);
			phnq.event.listen(s, "error", scriptLoadError);
		}
		
		s.type="text/javascript";
		s.charset="utf-8";
		s.src = phnq.encodeUrl(uri);
		h.appendChild(s);
	};
	
	var doAjaxReq = function(uri, params, options)
	{
		options = options || {};
		var method = options.method || "GET";
		var callback = options.callback;
		var postParams = method == "GET" ? null : params;
		params = method == "GET" ? params||{} : null;
		
		var req = typeof(XMLHttpRequest) == "undefined" ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
		
		req.onreadystatechange = function()
		{
			switch(req.readyState)
			{
				case 4:
					if(callback)
					{
						callback(req.responseXML || req.responseText, req);
					}
					break;
			}
		};
		
		req.open(method, phnq.encodeUrl(new phnq.util.URI(uri, params).toString()), typeof(callback)=="function");
		
		var body = "";
		
		if(method == "POST")
		{
			if(options.postObject)
			{
				req.setRequestHeader("Content-Type", "application/json");
				body = phnq.util.serialize(postParams);
			}
			else
			{
				req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				body = new phnq.util.URI("x", postParams).getQueryString();
			}
		}
		
		req.send(body);
		
		return req;
	};
	
})();
